import datetime

from decimal import Decimal

from django.db import transaction
from django_redis import get_redis_connection
from rest_framework import serializers

from goods.models import SKU
from orders.models import OrderInfo, OrderGoods
from users.models import User, Address


class OrderSettlement(serializers.ModelSerializer):
    """订单结算商品序列化器类"""
    count = serializers.IntegerField(label='商品结算数量')

    class Meta:
        model = SKU
        fields = ('id','name','price','default_image_url','count')


class OrderSaveSerializer(serializers.ModelSerializer):
    """订单创建保存序列化器"""
    class Meta:
        model = OrderInfo
        fields = ('order_id','pay_method','address')

        # 订单号不再采用数据库自增主键,而是由后端生成创建.所以要设置参数read_only=True
        read_only_fields = ('order_id',)

        extra_kwargs = {
            'address':{
                'write_only':True
            },
            'pay_method':{
                'write_only':True
            }
        }

    def create(self, validated_data):
        # 组织订单基本信息的参数
        user = self.context['request'].user
        order_id = datetime.datetime.now().strftime('%Y%m%d%H%M%S')+'%010d'%user.id

        address = validated_data['address'] # 查出来的是一个地址对象:因为反序列化自动将外键字段转成对象形式
        pay_method = validated_data['pay_method']

        # 先将总数量和总金额设置为0,下单成功后再改
        total_count =0
        total_amount =0

        # 运费
        freight = Decimal(10.00)
        # 指定status订单状态
        if pay_method == OrderInfo.PAY_METHODS_ENUM['CASH']:
            status = OrderInfo.ORDER_STATUS_ENUM['UNSEND']
        else:
            status = OrderInfo.ORDER_STATUS_ENUM['UNPAID']

        # with语句块下的代码，凡是涉及到数据库操作的代码，在进行数据库操作时，都会放在同一个事务中
        with transaction.atomic():
            # 创建一个事务保存点,万一那个修改数据库操作出错,需要撤销到保存点
            sid = transaction.savepoint()

            try:
                # 2.1 将订单数据保存到订单基本信息表中
                order = OrderInfo.objects.create(
                    order_id=order_id,
                    user=user,
                    address=address,
                    total_count=total_count,
                    total_amount=total_amount,
                    freight=freight,
                    pay_method=pay_method,
                    status=status
                )

                # 2.2 将订单的商品保存到订单商品表中
                # 从redis中取出购物车勾选的商品id
                redis_conn = get_redis_connection('cart')
                cart_selected_userid = 'cart_selected_%s' % user.id
                sku_ids = redis_conn.smembers(cart_selected_userid)

                # {
                #     b'<sku_id>': b'<count>',...}
                cart_userid = 'cart_%s' % user.id
                cart_redis = redis_conn.hgetall(cart_userid)

                for sku_id in sku_ids:

                    count = cart_redis[sku_id]
                    count = int(count)

                    # 根据sku_id获取sku对象
                    for i in range(3):

                        sku = SKU.objects.get(id=sku_id)

                        # # select * from tb_sku where id=<sku_id> for update;
                        # print('user: %s try get lock' % user.id)
                        # # 悲观锁-给sku商品数据加个锁,只能整个事务完成后,再释放锁给他人使用
                        # sku = SKU.objects.select_for_update().get(id=sku_id)

                        if count > sku.stock:# 库存判断
                            # 商品库存不足回滚到保存点
                            transaction.savepoint_rollback(sid)
                            raise serializers.ValidationError('商品库存不足,下单失败1')

                        # 记录原始库存
                        origin_stock = sku.stock
                        new_stock = origin_stock - count
                        new_sales = sku.sales + count

                        # 模拟订单并发问题
                        print('users: %s' % user.id)
                        import time
                        time.sleep(10)

                        # 更新库存 # res 返回影响的数据条数
                        res = SKU.objects.filter(id=sku_id, stock=origin_stock).update(stock=new_stock, sales=new_sales)
                        if res == 0:
                            if i == 2:
                                # 第三次失败回到保存点,并抛出异常
                                transaction.savepoint_rollback(sid)
                                raise serializers.ValidationError('想要修改库存三次都未修改成功. 下单失败3')
                            continue
                        # 修改成功跳出循环
                        break


                    price = sku.price

                    OrderGoods.objects.create(
                        order = order,
                        sku = sku,
                        count = count,
                        price = price
                    )
                    # 累加商品的总数和总额
                    total_count += count
                    total_amount += price*count

                # 商品实付款
                total_amount += freight
                order.total_count = total_count
                order.total_amount = total_amount
                order.save()
            # 先捕捉内层异常向外抛出
            except serializers.ValidationError:
                raise # 继续向外抛出异常
            except Exception:
                # 出错需要返回保存点sid
                transaction.savepoint_rollback(sid)
                raise serializers.ValidationError('数据库保存错误,下单失败2')

        # 2.3 将购物车redis中已提交的商品数据进行清除
        pl = redis_conn.pipeline()
        pl.hdel(cart_userid,*sku_ids)
        pl.srem(cart_selected_userid,*sku_ids)
        pl.execute()

        return order

class SaveCommentSerializer(serializers.ModelSerializer):
    """订单商品评价序列化器"""
    class Meta:
        model = OrderGoods
        fields = ('order','sku','comment','score','is_anonymous')

    def update(self, instance, validated_data):
        # 组织参数
        comment = validated_data.get('comment','')
        score = validated_data.get('score',5)
        is_anonymous = validated_data.get('is_anonymous',False)

        with transaction.atomic():
            try:# 修改订单商品的评论,星级和是否评价和是否匿名评价
                instance.comment = comment
                instance.score = score
                instance.is_anonymous = is_anonymous
                instance.is_commented = True
                instance.save()
                # 更改订单状态(必须当前订单商品评论完成后才可以修改订单状态)
                order_skus = OrderGoods.objects.filter(order=validated_data['order'])

                flag = False # 全部都评论了
                for order_sku in order_skus:
                    if not order_sku.is_commented:
                        # 有一个没有评论的就改为True
                        flag=True
                if not flag:# 如果全部都评论需要将订单状态改掉
                    instance.order.status = OrderInfo.ORDER_STATUS_ENUM.get('FINISHED')
                    instance.order.save()
                # 修改SKU商品的评论数
                instance.sku.comments += 1
                instance.sku.save()
                # 修改SPU的评论数
                instance.sku.goods.comments +=1
                instance.sku.goods.save()
            except Exception:
                raise serializers.ValidationError('更新评论数据失败')
        return instance

class OrderDetailSerializer3(serializers.ModelSerializer):
    """订单商品名称图片信息序列化器类"""
    class Meta:
        model = SKU
        fields = ('name','default_image_url')


class OrderDetailSerializer2(serializers.ModelSerializer):
    """(多)订单商品序列化器类"""
    sku = OrderDetailSerializer3(label='订单商品的基础信息')
    count = serializers.IntegerField(label='数量',read_only=True)
    price = serializers.DecimalField(max_digits=10, decimal_places=2,label='价格',read_only=True)

    class Meta:
        model = OrderGoods
        fields = ('count','price','sku')


class OrderDetailSerializer(serializers.ModelSerializer):
    """(一)订单信息序列化器类"""
    skus = OrderDetailSerializer2(label='订单商品的购买信息',many=True)

    class Meta:
        model = OrderInfo
        fields = ('order_id','total_amount','pay_method','freight','status','create_time','skus')
